#include "luaWrapper.h"
#include <stdlib.h>
namespace luacpp
{

std::map<void*,void*> ptrToUserData;
std::set<void*> userdataSet;
const char *table_userdata_info = "kenny.lualib.userdata_info";


/*---------------------------------------------------------------------------*/
/* debug helpers                                                             */
/*---------------------------------------------------------------------------*/
static void call_stack(lua_State* L, int n)
{
    lua_Debug ar;
    if (lua_getstack(L, n, &ar) == 1)
    {
        lua_getinfo(L, "nSlu", &ar);

        const char* indent;
        if (n == 0)
        {
            indent = "->\t";
            luaWrapper::print_error(L, "\t<call stack>");
        }
        else
        {
            indent = "\t";
        }

        if (ar.name)
            luaWrapper::print_error(L, "%s%s() : line %d [%s : line %d]", indent, ar.name, ar.currentline, ar.source, ar.linedefined);
        else
            luaWrapper::print_error(L, "%sunknown : line %d [%s : line %d]", indent, ar.currentline, ar.source, ar.linedefined);

        call_stack(L, n + 1);
    }
}


bool luaWrapper::dofile(const char *filename)
{
    if (luaL_dofile(lState, filename))
    {
        const char * error = lua_tostring(lState, -1);
        lua_pop(lState,1);
        printf("%s\n",error);
        return false;
    }
    return true;
}

/*---------------------------------------------------------------------------*/
void luaWrapper::print_error(lua_State *L, const char* fmt, ...)
{
    char text[4096];

    va_list args;
    va_start(args, fmt);
    vsprintf(text, fmt, args);
    va_end(args);

    lua_getglobal(L, "_ALERT");
    if (lua_isfunction(L, -1))
    {
        lua_pushstring(L, text);
        lua_call(L, 1, 0);
    }
    else
    {
        //ERROR_LOG("%s", text);
        lua_pop(L, 1);
    }
}

int luaWrapper::on_error(lua_State *L)
{
    print_error(L, "%s", lua_tostring(L, -1));

    call_stack(L, 0);

    return 0;
}
bool luaWrapper::dostring(char * str_buf)
{
    lua_pushcclosure(lState, on_error, 0);
    int errfunc = lua_gettop(lState);
    int len = strlen(str_buf);
    if (luaL_loadbuffer(lState, str_buf, len, "lua_tinker::dobuffer()") == 0)
    {
        if (lua_pcall(lState, 0, 0, errfunc) != 0)
        {
            lua_pop(lState, 1);
        }
    }
    else
    {
        print_error(lState, "%s", lua_tostring(lState, -1));
        lua_pop(lState, 1);
    }

    lua_pop(lState, 1);
    return true;
}

void luaWrapper::init()
{
    lState = luaL_newstate();
    luaL_openlibs(lState);

    lua_newtable(lState);
    lua_newtable(lState);
    lua_pushstring(lState,"__mode");
    lua_pushstring(lState,"v");
    lua_rawset(lState,-3);
    lua_setmetatable(lState,-2);
    lua_setglobal(lState,table_userdata_info);

}

void set_userdata(lua_State *L,void *ptr,int index)
{
    lua_pushvalue(L,index);
    lua_getglobal(L,table_userdata_info);
    lua_pushlightuserdata(L,ptr);  //push key
    lua_rotate(L,lua_gettop(L)-3,3);
    lua_rawset(L,-3);
    lua_pop(L,1);
}

void get_userdata(lua_State *L,void *ptr)
{
    lua_getglobal(L,table_userdata_info);
    lua_pushlightuserdata(L,ptr);  //push key
    lua_rawget(L,-2);
    lua_replace(L,-2);
}

int pushObj(lua_State *L,const void *ptr,const char *classname)
{
    objUserData<void> *userdata = (objUserData<void>*)ptrToUserData[(void*)ptr];
    if(!userdata)
    {
        size_t nbytes = sizeof(objUserData<void>);
        userdata = (objUserData<void> *)lua_newuserdata(L, nbytes);
        userdata->ptr = const_cast<void*>(ptr);
        userdata->construct_by_lua = false;
        ptrToUserData[(void*)ptr] = userdata;
        userdataSet.insert((void*)userdata);
        set_userdata(L,(void*)userdata,-1);
        luaL_setmetatable(L,classname);
    }
    else
    {
        get_userdata(L,(void*)userdata);
    }
    return 1;

}

void get_luatable(luatable &lt,lua_State *L)
{
    lt = popvalue<luatable>(L);
}
}
